import numpy as np

from optimizer.BaseOptimizer import BaseOptimizer
from utils.Ising import Ising


class Optimizer(BaseOptimizer):
    """
    论文：Binary Optimization Using Hybrid Grey Wolf Optimization for Feature Selection
    """
    optimizer_name = 'BGWOPSO'

    def __init__(self, config: dict):
        self.ising_J = np.zeros((1, 1))
        super().__init__(config)

        self.alpha_pos = np.zeros((1, self.n_dim))
        self.alpha_score = 0

        self.beta_pos = np.zeros((1, self.n_dim))
        self.beta_score = 0

        self.delta_pos = np.zeros((1, self.n_dim))
        self.delta_score = 0

        self.xs = np.random.randint(0, 2, (self.n_part, self.n_dim), dtype=np.int)
        self.vs = np.random.random((self.n_part, self.n_dim)) * 0.3

        self.w = 0.5 + np.random.random() / 2

    def run_once(self, actions=None):
        self.clip()
        self.best_update()

        for i in range(self.n_part):
            fitness = self.fits[i]
            if fitness < self.alpha_score:
                # print('alpha update')
                self.alpha_score = fitness
                self.alpha_pos = self.xs[i].copy()

            elif self.alpha_score < fitness < self.beta_score:
                # print('beta update')
                self.beta_score = fitness
                self.beta_pos = self.xs[i].copy()

            elif self.alpha_score < fitness < self.delta_score and fitness > self.beta_score:
                # print('delta update')
                self.delta_score = fitness
                self.delta_pos = self.xs[i].copy()

        for i in range(self.n_part):
            a = np.ones(self.n_dim) * 2 * (1 - self.fe_num / self.fe_max)
            r1 = np.random.random(self.n_dim)
            a1 = 2 * a * r1 - a
            c1 = 0.5
            d_alpha = abs(c1 * self.alpha_pos - self.w * self.xs[i])
            v1 = sigmf(-a1 * d_alpha) > np.random.random(self.n_dim)
            x1 = (v1 + self.alpha_pos) > 1

            r1 = np.random.random(self.n_dim)
            a2 = 2 * a * r1 - a
            c2 = 0.5
            d_beta = abs(c2 * self.beta_pos - self.w * self.xs[i])
            v2 = sigmf(-a2 * d_beta) > np.random.random(self.n_dim)
            x2 = (v2 + self.beta_pos) > 1

            r1 = np.random.random(self.n_dim)
            a3 = 2 * a * r1 - a
            c3 = 0.5
            d_delta = abs(c3 * self.delta_pos - self.w * self.xs[i])
            v3 = sigmf(-a3 * d_delta) > np.random.random(self.n_dim)
            x3 = (v3 + self.delta_pos) > 1

            r1 = np.random.random(self.n_dim)
            r2 = np.random.random(self.n_dim)
            r3 = np.random.random(self.n_dim)
            self.vs[i] = self.w * (self.vs[i] +
                                   c1 * r1 * (x1 - self.xs[i]) +
                                   c2 * r2 * (x2 - self.xs[i]) +
                                   c3 * r3 * (x3 - self.xs[i]))
            new_x = (sigmf((x1 + x2 + x3) / 3) + self.vs[i]) > np.random.random(self.n_dim)
            self.xs[i] = new_x * 1


def sigmf(x):
    return 1 / (1 + np.exp(-10 * (x - 0.5)))
